home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / sound / aiff writer sdev / aiff_writer.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  19.4 KB  |  589 lines

  1. /*
  2.     File:        AIFF_writer.c
  3.  
  4.     Contains:    Routines for dispatching a sound hardware output ('sdev') component.    
  5.  
  6.     Written by: Mark Cookson--based on code by Kip Olson    
  7.  
  8.     Copyright:    Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/16/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23. #include <Memory.h>
  24. #include <Errors.h>
  25. #include <Sound.h>
  26. #include <SoundInput.h>
  27. #include <Components.h>
  28. #include <Timer.h>
  29. #include <FixMath.h>
  30. #include <Files.h>
  31. #include <Script.h>
  32. #include <SoundComponents.h>
  33.  
  34. #ifndef __AIFFWRITER__
  35. #include "AIFF_writer.h"
  36. #endif
  37.  
  38. #ifndef __COMPONENTPROTOTYPES__
  39. #include "ComponentPrototypes.h"
  40. #endif
  41.  
  42. #ifndef __HARDWARE__
  43. #include "Hardware.h"
  44. #endif
  45.  
  46. #ifndef __STRUCTURES__
  47. #include "Structures.h"
  48. #endif
  49.  
  50. #define DEBUG        false
  51. #define FULLDEBUG    false
  52.  
  53. //This is needed for Think C, Metrowerks has the cool :__a1 operator
  54. //Ptr GetRegisterA1(void) = {0x2009};    /* MOVE.L A1,D0 */
  55.  
  56. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  57. // Main component Entry Point
  58. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  59.  
  60. #if !GENERATINGPOWERPC
  61. //----------------------------------------------------------------------------------
  62. // main
  63. //----------------------------------------------------------------------------------
  64. pascal ComponentResult main( ComponentParameters *params, SoundComponentGlobalsPtr globals );
  65. pascal ComponentResult main( ComponentParameters *params, SoundComponentGlobalsPtr globals ) {
  66.     return (SoundComponentEntryPoint(params,globals));
  67. }
  68. #endif
  69.  
  70. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  71. // Component Manager Methods
  72. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  73.  
  74. /*    ==============================================================================
  75.     SoundComponentOpen
  76.  
  77.     This routine is called when the Component Manager creates an instance of this
  78.     component. The routine should allocate global variables in the appropriate heap
  79.     and call SetComponentInstanceStorage() so the Component Manager can remember
  80.     the globals and pass them to all the method calls.
  81.     
  82.     Determining the heap to use can be tricky. The Component Manager will normally
  83.     load the component code into the system heap, which is good, since many applications
  84.     will be sharing this component to play sound. In this case, the component's global
  85.     variable storage should also be created in the system heap.
  86.  
  87.     However, if system heap memory is tight, the Component Manager will load
  88.     the component into the application heap of the first application that plays sound.
  89.     When this happens, the component should create global storage in the application heap
  90.     instead. The Sound Manager will make sure that other applications will not try
  91.     to play sound while the component is in this application heap.
  92.  
  93.     To determine the proper heap to use, call GetComponentInstanceA5(). If the value
  94.     returned is 0, then the component was loaded into the system heap, and all storage
  95.     should be allocated there. If the value returned is non-zero, the component is in
  96.     the application heap specifed by returned A5 value, and all storage should be
  97.     allocated in this application heap.
  98.     
  99.     NOTE: If the component is loaded into the application heap, the value returned by
  100.     GetComponentRefCon() will be 0.
  101.     NOTE: Do not attempt to initialize or access hardware in this call, since the
  102.     Component Manager will call SoundComponentOpen() BEFORE calling RegisterComponent().
  103.     Instead, initialize the hardware during InitOutputDevice(), described below.
  104.     NOTE: This routine is never called at interrupt time.
  105.     ============================================================================== */
  106.  
  107. pascal ComponentResult __SoundComponentOpen(void *unused1, ComponentInstance self) {
  108. #pragma unused (unused1)
  109.  
  110.     Handle                        h;
  111.     SoundComponentGlobalsPtr    globals;
  112.     long                        a5 = GetComponentInstanceA5(self);    // find out if we were loaded in app heap or system heap
  113.     ComponentResult                result = noErr;
  114.  
  115. #if DEBUG
  116. DebugStr ("\pIn __SoundComponentOpen;g");
  117. #endif
  118.  
  119.     h = NewHandleLockClear(sizeof(SoundComponentGlobals), a5 == 0);    // get space for globals in appropriate heap
  120.     if (h != nil) {
  121.         globals = (SoundComponentGlobalsPtr) *h;
  122.         globals->globalsHandle = h;                                // remember the handle
  123.         globals->inSystemHeap = (a5 == 0);                        // remember which heap we are in
  124.  
  125.         globals->hwGlobals = GetHardwareGlobals(self, globals->inSystemHeap);    // get hardware globals
  126.  
  127.     #ifdef FakeInterrupts
  128.         gGlobals = globals;
  129.     #endif
  130.  
  131.         SetComponentInstanceStorage (self, (Handle) globals);     // save pointer to our globals
  132.     } else {
  133.         result = MemError();
  134.     }
  135.  
  136. #if DEBUG
  137.     if (result != noErr) {
  138.         DebugStr ("\pGot an error in __SoundComponentOpen");
  139.     }
  140. #endif
  141.  
  142.     return (result);
  143. }
  144.  
  145. /*    ==============================================================================
  146.     SoundComponentClose
  147.  
  148.     This routine is called when the Component Manager is closing the instance of
  149.     this component. The routine should make sure all remaining data is written
  150.     to the hardware and that the hardware is completely turned off. It should
  151.     delete all global storage and close any other components that were opened.
  152.     
  153.     NOTE: Be sure to check that the globals pointer passed in to this routine is
  154.     not set to NIL. If the SoundComponentOpen() routine fails for any reason, the Component
  155.     Manager will call this routine passing in a NIL for the globals.
  156.     NOTE: This routine is never called at interrupt time.
  157.     ============================================================================== */
  158.  
  159. pascal ComponentResult __SoundComponentClose(SoundComponentGlobalsPtr globals, ComponentInstance self) {
  160. #if DEBUG
  161. DebugStr ("\pIn __SoundComponentClose;g");
  162. #endif
  163.  
  164.     if (globals) {                                                // we have some globals
  165.         ReleaseHardware(globals);                                // make sure the hardware is off and release it
  166.  
  167.         if (globals->sourceComponent)
  168.             CloseMixerSoundComponent(globals->sourceComponent);    // close mixer
  169.  
  170.         SaveHardwareGlobals(self, globals->hwGlobals);            // save preferences
  171.  
  172.         DisposeHandle(globals->globalsHandle);                    // torch our storage
  173.     }
  174.  
  175.     return (noErr);
  176. }
  177.  
  178. /*    ==============================================================================
  179.     SoundComponentRegister
  180.  
  181.     This routine is called once, usually at boot time, when the Component Manager
  182.     is first registering this component. This routine should check to see if the proper
  183.     hardware is installed and return 0 if it is. If the hardware is not installed,
  184.     the routine should return 1 and this component will not be registered. This is
  185.     also an opportunity to do one-time initializations and perhaps register this
  186.     component again if more than one hardware device is available. Global state information
  187.     can also be saved in the component refcon by calling SetComponentRefCon();
  188.  
  189.     NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
  190.     component in order for this routine to be called.
  191.     NOTE: This routine is never called at interrupt time.
  192.     ============================================================================== */
  193.  
  194. pascal ComponentResult __SoundComponentRegister(SoundComponentGlobalsPtr globals) {
  195. #pragma unused (globals)
  196.     NumVersion            installedVersion;
  197.     ComponentResult        result;
  198.  
  199. #if DEBUG
  200. DebugStr ("\pIn __SoundComponentRegister;g");
  201. #endif
  202.  
  203. //    Check for hardware here. We are always installed, so we return 0
  204.  
  205.     // we can only run if version 3.0 or greater of the sound manager is running
  206.     // we can check the entire long because the format of NumVersion is BCD data
  207.     
  208.     installedVersion = SndSoundManagerVersion();
  209.     if (installedVersion.majorRev >= 3) {                            // has 3.0 sound manager
  210.         result = kComponentWantsToRegister;
  211.     } else {
  212.         result = kComponentDoesNotWantToRegister;
  213.     }
  214.  
  215. #if DEBUG
  216.     if (result != noErr) {
  217.         DebugStr ("\pGot an error in __SoundComponentRegister");
  218.     }
  219. #endif
  220.  
  221.     return result;
  222. }
  223.  
  224. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  225. // Standard Component Methods
  226. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  227.  
  228. /*    ==============================================================================
  229.     InitOutputDevice
  230.  
  231.     This routine is called once when the Sound Manager first opens this component.
  232.     The routine should initialize the hardware to default values, allocate the
  233.     appropriate mixer component and create any other memory that is required.
  234.  
  235.     NOTE: This routine is never called at interrupt time.
  236.     ============================================================================== */
  237.  
  238. pascal ComponentResult __InitOutputDevice(SoundComponentGlobalsPtr globals, long actions) {
  239. #pragma unused (actions)
  240.     ComponentResult        result;
  241.  
  242. #if DEBUG
  243. DebugStr ("\pIn __InitOutputDevice;g");
  244. #endif
  245.  
  246.     // make sure we got our globals
  247.     if (globals->hwGlobals != nil) {
  248.         // setup the hardware
  249.         result = SetupHardware(globals);
  250.         if (result == noErr) {
  251.             // first create a mixer and tell it the type of data it should output. The
  252.             // description includes sample format, sample rate, sample size, number of channels
  253.             // and the size of your optimal interrupt buffer. If a mixer cannot be found that
  254.             // will output this type of data, an error will be returned.
  255.  
  256.             result = OpenMixerSoundComponent(&globals->thisSifter, 0, &globals->sourceComponent);
  257.         } else {
  258.             result = result;
  259.         }
  260.     } else {
  261.         result = notEnoughHardwareErr;
  262.     }
  263.  
  264. #if DEBUG
  265.     if (result != noErr) {
  266.         DebugStr ("\pGot an error in __InitOutputDevice");
  267.     }
  268. #endif
  269.  
  270.     return (result);
  271. }
  272.  
  273. /*    ==============================================================================
  274.     SoundComponentGetInfo
  275.  
  276.     This routine returns information about this component to the Sound Manager. A
  277.     4-byte OSType selector is used to determine the type and size of the information
  278.     to return. If the component does not support a selector, it should delegate this
  279.     call on up the component chain.
  280.  
  281.     NOTE: This can be called at interrupt time. However, selectors that return
  282.     a handle will not be called at interrupt time.
  283.     ============================================================================== */
  284.  
  285. pascal ComponentResult __SoundComponentGetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) {
  286.     HandleListPtr        listPtr;
  287.     short                *sp,
  288.                         i;
  289.     UnsignedFixed        *lp;
  290.     Handle                h;
  291.     HardwareGlobalsPtr    hwGlobals    = globals->hwGlobals;
  292.     ComponentResult        result        = noErr;
  293.  
  294. #if DEBUG
  295. char sel[5];
  296. DebugStr ("\pIn __SoundComponentGetInfo;g");
  297. sel[0] = 4;
  298. BlockMoveData (&selector, &(sel[1]), 4);
  299. #endif
  300.  
  301.     // make sure we got our globals
  302.     if (hwGlobals != nil) {
  303.         switch (selector) {
  304.             case siSampleSize:                                // return current sample size
  305.                 *((short *) infoPtr) = hwGlobals->sampleSize;
  306.                 break;
  307.  
  308.             case siSampleSizeAvailable:                        // return samples sizes available
  309.                 h = NewHandle(sizeof(short) * kSampleSizesCount);    // space for sample sizes
  310.                 if (h != nil) {
  311.                     listPtr = (HandleListPtr) infoPtr;
  312.                     listPtr->count = 0;                            // no. sample sizes in handle
  313.                     listPtr->handle = h;                        // handle to be returned
  314.  
  315.                     sp = (short *) *h;                            // store sample sizes in handle
  316.  
  317.                     for (i = 0; i < kSampleSizesCount; ++i) {
  318.                         if (hwGlobals->sampleSizesActive[i]) {
  319.                             listPtr->count++;
  320.                             *sp++ = hwGlobals->sampleSizes[i];
  321.                         }
  322.                     }
  323.                 } else {
  324.                     result = MemError();
  325.                 }
  326.                 break;
  327.  
  328.             case siSampleRate:                                // return current sample rate
  329.                 *((Fixed *) infoPtr) = hwGlobals->sampleRate;
  330.                 break;
  331.  
  332.             case siSampleRateAvailable:                        // return sample rates available
  333.                 h = NewHandle(sizeof(UnsignedFixed) * kSampleRatesCount);    // space for sample rates
  334.                 if (h != nil) {
  335.                     listPtr = (HandleListPtr) infoPtr;
  336.                     listPtr->count = 0;                            // no. sample rates in handle
  337.                     listPtr->handle = h;                        // handle to be returned
  338.  
  339.                     lp = (UnsignedFixed *) *h;
  340.  
  341.                     // If the hardware can support a range of sample rate values, then the
  342.                     // list count should be set to zero and the min and max sample rate values
  343.                     // should be stored in the handle.
  344.  
  345.                     if (hwGlobals->supportsRateRange) {
  346.                         *lp++ = hwGlobals->sampleRateMin;
  347.                         *lp++ = hwGlobals->sampleRateMax;
  348.                     } else {
  349.                         // If the hardware supports a limited set of sample rates, then the list count
  350.                         // should be set to the number of sample rates and this list of rates should be
  351.                         // stored in the handle.
  352.                         for (i = 0; i < kSampleRatesCount; ++i) {
  353.                             if (hwGlobals->sampleRatesActive[i]) {
  354.                                 listPtr->count++;
  355.                                 *lp++ = hwGlobals->sampleRates[i];
  356.                             }
  357.                         }
  358.                     }
  359.                 } else {
  360.                     result = MemError();
  361.                 }
  362.                 break;
  363.  
  364.             case siNumberChannels:                            // return current no. channels
  365.                 *((short *) infoPtr) = hwGlobals->numChannels;
  366.                 break;
  367.  
  368.             case siChannelAvailable:                        // return channels available
  369.                 h = NewHandle(sizeof(short) * kChannelsCount);    // space for channels
  370.                 if (h != nil) {
  371.                     listPtr = (HandleListPtr) infoPtr;
  372.                     listPtr->count = 0;                            // no. channels in handle
  373.                     listPtr->handle = h;                        // handle to be returned
  374.  
  375.                     sp = (short *) *h;                            // store channels in handle
  376.  
  377.                     for (i = 0; i < kChannelsCount; ++i) {
  378.                         if (hwGlobals->channelsActive[i]) {
  379.                             listPtr->count++;
  380.                             *sp++ = hwGlobals->channels[i];
  381.                         }
  382.                     }
  383.                 } else {
  384.                     result = MemError();
  385.                 }
  386.                 break;
  387.  
  388.             case siHardwareVolume:
  389.                 *((long *)infoPtr) = hwGlobals->volume;
  390.                 break;
  391.  
  392.             // if you do not handle this selector, then delegate it up the chain
  393.             default:
  394.                 result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  395.                 break;
  396.         }
  397.     } else {
  398.         result = notEnoughHardwareErr;
  399.     }
  400.  
  401. #if DEBUG
  402.     if (result != noErr) {
  403.         DebugStr ("\pGot an error in __SoundComponentGetInfo, selector was: ;g");
  404.         DebugStr ((unsigned char*)sel);
  405.     }
  406. #endif
  407.  
  408.     return (result);
  409. }
  410.  
  411. /*    ==============================================================================
  412.     SoundComponentSetInfo
  413.  
  414.     This routine sets information about this component. A 4-byte OSType selector is
  415.     used to determine the type and size of the information to apply. If the component
  416.     does not support a selector, it should delegate this call on up the component chain.
  417.  
  418.     NOTE: This can be called at interrupt time.
  419.     ============================================================================== */
  420.  
  421. pascal ComponentResult __SoundComponentSetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) {
  422.     HardwareGlobalsPtr        hwGlobals = globals->hwGlobals;
  423.     ComponentResult            result = noErr;
  424.     short                    i;
  425.  
  426. #if DEBUG
  427. DebugStr ("\pIn __SoundComponentSetInfo;g");
  428. #endif
  429.  
  430.     // make sure we got our globals
  431.     if (hwGlobals != nil) {
  432.         switch (selector) {
  433.             case siSampleSize:                                // set sample size
  434.             {
  435.                 short    sampleSize = (short) infoPtr;
  436.                 
  437.                 result = siInvalidSampleSize;
  438.                 for (i = kSampleSizesCount - 1; i >= 0; --i) {
  439.                     if ((hwGlobals->sampleSizesActive[i]) &&
  440.                         (hwGlobals->sampleSizes[i] == sampleSize)) {
  441.                         hwGlobals->sampleSize = sampleSize;
  442.                         hwGlobals->dirty = true;
  443.                         result = noErr;
  444.                         break;
  445.                     }
  446.                 }
  447.                 break;
  448.             }
  449.  
  450.             case siSampleRate:                                // set sample rate
  451.             {
  452.                 UnsignedFixed    sampleRate = (UnsignedFixed) infoPtr;
  453.  
  454.                 if (hwGlobals->supportsRateRange) {
  455.                     if ((hwGlobals->sampleRateMin <= sampleRate) && (sampleRate <= hwGlobals->sampleRateMax)) {
  456.                         hwGlobals->sampleRate = sampleRate;
  457.                         hwGlobals->dirty = true;
  458.                     } else {
  459.                         result = siInvalidSampleRate;
  460.                     }
  461.                 } else {
  462.                     result = siInvalidSampleRate;
  463.                     for (i = kSampleRatesCount - 1; i >= 0; --i) {
  464.                         if ((hwGlobals->sampleRatesActive[i]) &&
  465.                             (hwGlobals->sampleRates[i] == sampleRate)) {
  466.                             hwGlobals->sampleRate = sampleRate;
  467.                             hwGlobals->dirty = true;
  468.                             result = noErr;
  469.                             break;
  470.                         }
  471.                     }
  472.                 }
  473.                 break;
  474.             }
  475.  
  476.             case siNumberChannels:                            // set no. channels
  477.             {
  478.                 short    numChannels = (short) infoPtr;
  479.  
  480.                 result = notEnoughHardware;
  481.                 for (i = kChannelsCount - 1; i >= 0; --i) {
  482.                     if ((hwGlobals->channelsActive[i]) &&
  483.                         (hwGlobals->channels[i] == numChannels)) {
  484.                         hwGlobals->numChannels = numChannels;
  485.                         hwGlobals->dirty = true;
  486.                         result = noErr;
  487.                         break;
  488.                     }
  489.                 }
  490.                 break;
  491.             }
  492.  
  493.             case siHardwareVolume:
  494.                 DebugStr ("\pin siHardwareVolume");
  495.                 hwGlobals->volume = (long) infoPtr;
  496.                 hwGlobals->dirty = true;
  497.                 break;
  498.  
  499.             // if you do not handle this selector, then call up the chain
  500.             default:
  501.                 result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  502.                 break;
  503.         }
  504.     } else {
  505.         result = notEnoughHardwareErr;
  506.     }
  507.  
  508. #if DEBUG
  509.     if (result != noErr) {
  510.         DebugStr ("\pGot an error in __SoundComponentSetInfo");
  511.     }
  512. #endif
  513.  
  514.     return (result);
  515. }
  516.  
  517. /*    ==============================================================================
  518.     StartSource
  519.  
  520.     This routine is used to start sounds playing that are currently paused. It should
  521.     first delegate this call up the component chain so the rest of the chain can prepare
  522.     to play this sound. Then, if the hardware is not already started it should be
  523.     turned on.
  524.  
  525.     NOTE: This can be called at interrupt time.
  526.     ============================================================================== */
  527.  
  528. pascal ComponentResult __StartSource(SoundComponentGlobalsPtr globals, short count, SoundSource *sources) {
  529.     ComponentResult        result;
  530.  
  531. #if DEBUG
  532. DebugStr ("\pIn __StartSource;g");
  533. #endif
  534.  
  535.     // tell the mixer to start these sources
  536.     result = SoundComponentStartSource(globals->sourceComponent, count, sources);
  537.     if (result == noErr) {
  538.         // make sure hardware interrupts are running
  539.         StartHardware(globals);
  540.     }
  541.  
  542. #if DEBUG
  543.     if (result != noErr) {
  544.         DebugStr ("\pGot an error in __StartSource");
  545.     }
  546. #endif
  547.  
  548.     return (result);
  549. }
  550.  
  551. /*    ==============================================================================
  552.     PlaySourceBuffer
  553.  
  554.     This routine is used to specify a new sound to play and conditionally start
  555.     the hardware playing that sound. It should first delegate this call up the component
  556.     chain so the rest of the chain can prepare to play this sound. Then, if the
  557.     hardware is not already started it should be turned on.
  558.  
  559.     NOTE: This can be called at interrupt time.
  560.     ============================================================================== */
  561.  
  562. pascal ComponentResult __SoundComponentPlaySourceBuffer(SoundComponentGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) {
  563.     ComponentResult        result;
  564.  
  565. #if DEBUG
  566. DebugStr ("\pIn __SoundComponentPlaySourceBuffer;g");
  567. #endif
  568.  
  569.     // tell mixer to start playing this new buffer
  570.     result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
  571.     if (result == noErr) {
  572.         // if the kSourcePaused bit is set, then do not turn on your hardware just yet
  573.         // (the assumption is that StartSource() will later be used to start this sound playing).
  574.         // If this bit is not set, turn your hardware interrupts on.
  575.  
  576.         if (!(actions & kSourcePaused)) {
  577.             StartHardware(globals);
  578.         }
  579.     }
  580.  
  581. #if DEBUG
  582.     if (result != noErr) {
  583.         DebugStr ("\pGot an error in __SoundComponentPlaySourceBuffer");
  584.     }
  585. #endif
  586.  
  587.     return (result);
  588. }
  589.